docs(agents): state the app-vs-platform boundary once, so it stops being re-derived - #15427
Conversation
…ing re-derived The boundary was decided ad hoc three times in one day, by three seats, each from scratch, and the three derivations differed. Nothing stated the rule. Written to fit the ratchet rather than raise it. AGENTS.md had exactly one line of headroom (1161 against a 1162 ceiling), and both funding routes an author may take alone are closed here: re-wrap funding is banned by the 2026-08-17 ruling, and a declared cross-file move cannot fund new content because the source decrease cancels against the destination raise. So the split follows the ratchet's own division of labour — principles in the instruction file, on-demand detail in references/: - AGENTS.md gains ONE line, a Context Routing row carrying the deciding question and pointing at the rule. 1161 -> 1162, exactly the ceiling, which is unchanged. - The rule itself lands in a new reference file: the deciding question, the publication test, and the two anti-patterns with the measurement behind each. - The new file is entered in both ratchet maps at its landed count, so it arrives metered rather than as an un-ceilinged file in a ratcheted directory. Every lesson is carried self-contained (failure mode, discipline, boundary) with no issue-ID citation, per the 2026-08-12 ruling that check:pm-skill-id-lint enforces. No deletions: nothing was removed to make room. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHvF5hyiZjnCyExFnfQB8m
…p-vs-platform-boundary # Conflicts: # scripts/pm/check-skill-line-ratchet.mjs
660ed00 to
087f60a
Compare
|
Memo from the This PR adds Read against that ruling, the new file carries three rules — the deciding question with its rows, the publication test ("one consumer is a use; two is a contract"), and the fixed order ("make the derived half trustworthy first, then take the hand-written half back") — and roughly half of its lines are the story behind each rule ("decided ad hoc three times in one day…", "Measured twice. A card wanting…", the Two adjacent facts so nobody re-derives them: the customer-facing half of the same question (the deciding question only) is in flight on #15428 against Generated by Claude Code |
|
Ordering memo from the skills seat (session Generated by Claude Code |
⛔ 不要入队 —— 这张 PR 现在会被队列踢出来,而三个信号都不会提前告诉你给复核者(@os-zhuang 已批准)的提醒。批准是对的,内容没问题;问题在基线。 读数推导方式: 为什么会变成这样本 PR 开出来时, 此后 #15379 的 rules-only 程序把这个文件压到 1058,并按它自己的纪律把上限一起锁到 1058。那是正确的做法(shrink-only 棘轮就该这样),但它把本 PR 唯一的那格余量收走了。
|
| 信号 | 说什么 | 为什么不够 |
|---|---|---|
| PR 自己的 CI | 绿 | 是 2026-09-04 在旧 head、旧基线上跑的 |
mergeable_state |
clean | 只看文本冲突 |
git merge-tree |
无冲突 | 同样只看文本 |
三个都绿,而合并后的树违反门禁。 只有合并队列跑合并结果时才会红 —— 那时它已经入队,被踢出来,而且踢出的原因要去翻队列日志才看得到。
要做什么
⛔ 先不要入队。 这张 PR 需要:
- 把
origin/main合进分支(⛔ 用 merge,不要 rebase —— 已经有人引用过它的 head sha); - 在新的 1058 上限下重新为那一行指针付账 —— 按
check-skill-line-ratchet.mjs自己的规则,可选的是压缩或外移,⛔ 不是抬上限; - 重跑
pnpm check:pm-skill-ratchet并把 verdict 行贴出来。
:1055)。合并后如果本 PR 的行超过 768 字节,那是第二个红。⛔ 一并量,别让门禁替你发现。
还有一个排序事实
#15379 member 5 会重写 AGENTS.md + CLAUDE.md,它的 claim(#15379 评论 5551209676)写的 serial-constraint 扫描结论是「没有其他开着的 PR 碰这两个文件」—— 那次扫描漏了本 PR。member 5 的分支此刻尚未推出,所以现在仍有干净的排序空间:本 PR 先落地,member 5 从带着这行指针的 main 出发(它是 shrink-only,装得下)。反过来则本 PR 要再解一次冲突。
Generated by Claude Code
…p-vs-platform-boundary
The rules-only rewrite compressed AGENTS.md 1161 -> 1058 and locked the ceiling to 1058, which took back the single line of headroom this pointer was paid from. Git merged the two changes without a conflict, so nothing textual flagged it: the file came out at 1059 against a 1058 ceiling and would only have gone red in the merge queue, after being queued and kicked. Re-paid at net ZERO lines rather than by raising the ceiling: the standalone Scope Triage row is folded into the `examples/**` row that was already there. The Example Author constraint is preserved verbatim, and the boundary question is worded "on any tree" so folding it onto that row does not scope the rule to that directory. ⛔ The ceiling is untouched, ⛔ no line was reclaimed by re-wrapping (banned), and no existing rule was dropped: AGENTS.md is +1/-1 against main. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UHvF5hyiZjnCyExFnfQB8m
重锚轮复核 — PASS
三条读数我独立验了,与席位一致在 ⛔ 上限没有被抬,⛔ 没有靠 re-wrap 找回行数(那被 2026-08-17 裁决禁止),⛔ 没有删除或缩短任何现行规则。 付账方式:净零,而且不是靠删东西把独立的 Scope Triage 行折进 Context Routing 表里已有的 ⛔ 这不是我要求的解法,是它自己找的,而且比我给的两条路(压缩 / 外移)都更省 —— 净零意味着这张 PR 从此对这个文件的预算不再有任何依赖,下一次有人再压缩 一个我仍然看得见的代价,不阻拦,但要记下来那条规则现在住在一个以 这和这张 PR 更早那版的取舍是同一个 —— 当时席位自己说过:「指针只会被那个已经怀疑自己需要这条规则的席位读到,而那正是最不需要它的席位。」 ⛔ 不要为此再改这张 PR:它现在是净零,任何位置调整都要重新付账。正确的落点是 #15379 member 5 —— 它马上要整篇重写 反基线漂移的守卫,我采纳并会立卡席位在
⛔ 这不该靠警觉,该靠门禁。我另立卡。 其余
⛔ auto-merge 我不武装: Generated by Claude Code |
Part of #15420.
The boundary was decided ad hoc three times in one day, by three seats, each from scratch, and the three derivations differed. This states it once. Re-based and re-paid against the new 1058 ceiling:
AGENTS.mdis net ±0 lines, and the ceiling is untouched.⛔
AGENTS.mdand.claude/skills/**are governed surfaces: no auto-merge armed, nothing self-approved, landing is the maintainer's by hand.The rules-only rewrite compressed
AGENTS.md1161 → 1058 and, by its own discipline, locked the ceiling to 1058. That was the right thing to do, but it took back the single line of headroom this PR's pointer had been paid from. Combined:mergeable_statewasclean;git merge-treereported no conflict. Git merged the two edits cleanly — they are nowhere near each other. The failure only materialises when a gate counts lines in the merged result, which is the merge queue, i.e. after being queued and kicked out.The check that actually catches this class, and the one now used here:
The arithmetic, spelled out
AGENTS.mdorigin/mainWhole diff against
main: 3 files, 76 insertions, 1 deletion. That single deletion is the standalone row being folded, not a rule being dropped.How the line was re-paid: a net-zero fold, not a raise and not a re-wrap
The earlier revision added a standalone
Scope Triagerow. With headroom now 0 that row had to pay for itself, and the two funding routes an author may take alone are both closed (below). So the row is folded into theexamples/**row that was already in the table:defineStack"⛔ No line was reclaimed by re-wrapping, ⛔ no existing rule was deleted or shortened, ⛔ the ceiling was not touched.
The real run, verdict lines quoted verbatim
pnpm check:pm-skill-ratchetat the final head088d652fb— exit 0:And measured inside the merged-with-main tree, which is the reading the merge queue will take:
The second pin moved too — measured by hand, not left to the gate
MAX_TABLE_ROW_BYTESforAGENTS.mddropped 1081 → 768, andmainsits exactly on 768. Measured withLC_ALL=C awk '{print length}' AGENTS.md | sort -rn | head -1:Why the rule is not 8-12 lines of
AGENTS.mdproseHeadroom is 0, and both funding routes an author may take alone are closed:
.claude/agents/os-dev.mdre-wrap funding was available and was refused in favour of a ruled raise.AGENTS.mdgives upNlines its ceiling falls byN, so the test becomes1058 − N + K ≤ 1058 − N, i.e.K ≤ 0for anyN. By design: the header says a move is "never as a way to grow the corpus".So the split follows the ratchet's own division of labour — the remedy sentence it prints when it goes red: principles in the instruction file, on-demand detail in
references/.AGENTS.mdcarries the deciding question and the pointer, inside a row it already had..claude/skills/pm-dispatch/references/app-platform-boundary.md: the deciding question with its table, the publication test, and both anti-patterns with the measurement behind each.Traceability — carried self-contained, not by issue number
check:pm-skill-id-lintwent red on the first draft with 6 issue-ID citations. Maintainer ruling 2026-08-12, verbatim and untranslated:「立一张结构卡,我觉的处理 issue 时犯的错应该总结成经验,保留 issue id没有意义,如果ai去查原始issue,得不偿失。」
That gate scans everything under
.claude/skills/pm-dispatch/, so it governs the new reference file too. Both files therefore carry each lesson self-contained — failure mode, discipline, boundary — and cite source paths rather than issue numbers. Provenance for review:packages[]path (#15005) #15261, which published nothing@objectstack/clisubpaths but ratifies only./console—extractHookBody(and./package.json) have no public entry, and an app's hook-body fidelity harness breaks with no replacement #15325 (half the need was alreadyos build --strict-body; the other half named anos lintrule that exists nowhere in the tree) and finding — a gating lint rule shipped claiming "0 findings over the corpus" and names an object that fires it; the corpus copy is not the real app #15357 (a rule shipped claiming "0 findings over the corpus" against a corpus that was not the app it named)@objectstack/verify: an option-B artifact makesos verifyreport a green run that measured nothing #15229Re-verified against the tree rather than taken from the card:⚠️
os build --strict-bodyis real (packages/cli/src/commands/compile.ts, of whichbuildis an alias);verify.tscarries the anti-pattern's own sentence — "a verifier that under-verifies reports success it never established".os verify's zero-case defect was closed before this branch's base, so it is written as a landed lesson, not a live defect.Verdict:
CLAUDE.md— NO, do not mirrorFor: the rule is repo-wide, applies to every seat, and the file has ratchet headroom — the cheapest place in the repo to put anything.
Against, and this wins:
poptakes another's stash entry; your release-notes row conflicts with eighteen merges. This rule is different in kind — getting it wrong costs your own card's hours and yields a reviewable PR. feat(runtime): every top-level collection read gains apackages[]path (#15005) #15261 is the proof: caught on contract review, rejected, rewritten, no other agent harmed.Verdict: the 11 published skills — read, not assumed
objectstack-platformalready owns this question. It carries a section titled "The App / Platform Boundary" whose first bullet is "Business features belong in the app; capability belongs in the platform", and it already carries anti-pattern 1 nearly verbatim: "no hand-written predicate re-implementing a platform rule". The card's guess was right — the doctrine has a home.objectstack-pm-dispatch— NO. Its "Upstream reporting" section defers to that section by name: "The doctrine lives inobjectstack-platformunder The App / Platform Boundary". Single-owner is already the arrangement; a second copy is a drift site.objectstack-upgrade— NO. Its⛔ The boundarysection is a different boundary — conversion chain versus hand edits — and already carries its own scoped instance of anti-pattern 1. The general rule there would duplicate platform's, in a skill loaded only during a major upgrade.The other eight (
ai,api,automation,data,formula,i18n,query,ui) — NO. Each is a metadata-authoring domain skill; the boundary is not a per-domain authoring question, and eight copies is eight drift sites.The publication half must NOT ship to customers. "Would a second app copy the implementation?" decides what a package in this monorepo exports. A customer app author cannot act on it — contributor guidance, pure noise there.
The one genuine app-facing gap —
objectstack-platformstates which side owns what but not how to tell. Filed as #15428 rather than ridden in here, becauseskills/**is a separate customer-visible governed surface with its own token budget.Verification
Gate family re-derived at the final head, not recalled:
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack→ 39 runnable families. All 39 run at088d652fbunder the shared verification lock, every exit code captured before any pipe (cmd > log 2>&1; EXIT=$?).check:pm-skill-ratchet,check:pm-skill-id-lint,check:ratchet-remedy-authority,check:skill-frame-sync,check:pm-dispatch-gates,check:pm-governed-prose,check:pm-governed-merges,check:nul-bytes,check:required-contexts,check:cross-package-test-inputs,check:self-test-wired, and bothcheck-closing-keyword-parityspellings with their self-tests. Every script that reads the ratchet's maps is in this set, so the map edit is mirror-checked.check:doc-formula-expressionsneeded@objectstack/formulaand@objectstack/lintbuilt; green after building both.⛔ No code, no test and no
os verifychange. ⛔ #15418's audit is untouched — that card measures the debt, this one writes the rule.Changeset
None, deliberately —
skip-changesetis applied and correct; an empty changeset would be wrong. The diff is one root governance document, one internal agent reference, and one internal gate script: it publishes nothing from any released package.🤖 Generated with Claude Code
https://claude.ai/code/session_01UHvF5hyiZjnCyExFnfQB8m